package drp.snowbell.entity;


import com.google.appengine.repackaged.org.json.JSONException;
import com.google.appengine.repackaged.org.json.JSONObject;

import drp.snowbell.cfg.FullTextConfig;
import drp.snowbell.cfg.PropertyConfig;
import drp.snowbell.exception.CatchException;
import drp.snowbell.util.BaseFieldType;
import drp.snowbell.util.DbType;
import drp.snowbell.util.StrHelper;

/**
 * 模块的属性类.<b>可以尝试更改为泛型</b>
 * 
 * @author woj
 * 
 */
public class Property {

	public final static String PROPERTY_NAME = "Name";
	public final static String PROPERTY_ID = "Id";
	public final static String PROPERTY_DELETED = "Deleted";
	public final static String PROPERTY_STORAGE_VERSION = "StorageVersion";

	private final String name;// 属性名称,第一个字符大写
	private final String label;
	private final String dbFieldName;// 数据库字段名称
	private final DbType dbType;
	private final int dbLength;
	private final BaseFieldType valueType;
	private final boolean isUnique;
	private final boolean isRequired;
	private final String relationExpString;
	private RelationExp relationExp;
	private final String dbDefaultValue;
	private final String setterName;// setter函数名. 为空的话根据name推断
	private final String getterName;// getter函数名. 为空的话根据name推断
	private final boolean isSkill;
	private final boolean isDbField; // 是否是数据库字段
	/** 是否只读.数据创建以后就不允许修改(譬如用户昵称) */
	private final boolean isReadOnly;
	/** 全文搜索索引配置 */
	private final FullTextConfig fullTextConfig;
	/**
	 * 所属的结构名称.如果isSkill为true的话返回的是SkillName,否则返回的是beanName.
	 */
	private final String entityName;
	private final String skillName;

	private final boolean isEditable;

	/**
	 * 构造函数
	 * 
	 * @param parentClass
	 *            属性所属类的Class. 可能是ITBean也有可能是ISkill
	 * @param pc
	 *            属性配置
	 * @param entityName
	 *            实体名称
	 * @param skillName
	 *            技能名称
	 */
	public Property(Class<?> parentClass, PropertyConfig pc, String entityName, String partName) {

		this.name = pc.getName();
		this.label = pc.getLabel();
		this.dbType = DbType.valueOf(pc.getDbType());
		this.valueType = BaseFieldType.valueOf(pc.getValueType());
		this.isUnique = pc.getIsUnique();
		this.isRequired = pc.getIsRequired();
		this.isReadOnly = pc.getIsReadOnly();
		this.entityName = entityName;
		this.dbDefaultValue = pc.getDbDefaultValue();
		this.skillName = skillName;
		this.isSkill = !StrHelper.isNullOrEmpty(skillName);
		this.dbLength = dbLength;

		this.relationExpString = relationExpString;
		// if (!StrHelper.isNullOrEmpty(relationExpString)) {
		// this.relationExp = new RelationExp(this, relationExpString);
		// } else {
		// this.relationExp = null;
		// }
		this.dbFieldName = dbFieldName;
		FullTextConfig ftCfg = new FullTextConfig();
		if (!StrHelper.isNullOrEmpty(fullTextConfig)) {
			boolean isEnable = false;
			if (fullTextConfig.charAt(0) == '{') {
				try {
					JSONObject json = new JSONObject(fullTextConfig);
					ftCfg.setPower(json.optInt("power"));
					// 如果开启全文搜索功能
					isEnable = json.optBoolean("enable");

				} catch (JSONException e) {
					CatchException.take(e);
					ftCfg.setEnable(false);
				}
			} else {
				if (fullTextConfig.equalsIgnoreCase("true")) {
					isEnable = true;
				}
				ftCfg.setPower(1);// 默认权重
			}
			if (!Config.get().getIsUseFullText()) {
				isEnable = false;
			}
			ftCfg.setEnable(isEnable);
		}
		this.fullTextConfig = ftCfg;
		// Field tempField = null;
		// if (isSkill) {
		// SkillDna skillDna = SkillManager.getSkillDna(beanName, skillName);
		// try {
		// tempField = skillDna.getSkillClass().getDeclaredField(name);
		// } catch (Exception e) {
		// CatchException.take(e);
		// }
		//
		// } else {
		// Dna dna = Farm.getDna(beanName);
		// try {
		// tempField = dna.getBeanClass().getDeclaredField(name);
		// } catch (Exception e) {
		// CatchException.take(e);
		// }
		// }

		this.isDbField = (!StrHelper.isNullOrEmpty(this.dbFieldName)) && StrHelper.isNullOrEmpty(relationExpString);

		if (StrHelper.isNullOrEmpty(setterName)) {
			setterName = "get" + name;
		}
		if (StrHelper.isNullOrEmpty(getterName)) {
			getterName = "get" + name;
		}
		this.setterName = setterName;
		this.getterName = getterName;

		if (PROPERTY_DELETED.equals(name) || PROPERTY_ID.equals(name) || PROPERTY_NAME.equals(name) || PROPERTY_STORAGE_VERSION.equals(name)) {
			// 如果是系统属性则不能编辑
			this.isEditable = false;
		} else {
			this.isEditable = isEditable;
		}
	}

	/**
	 * 属性名称,第一个字符大写
	 * 
	 * @return 属性名称
	 */
	public String getName() {
		return name;
	}

	/**
	 * 标签
	 * 
	 * @return 标签
	 */
	public String getLabel() {
		return label;
	}

	/**
	 * 返回字段名称
	 * 
	 * @return 字段名称
	 */
	public String getDbFieldName() {
		return dbFieldName;
	}

	/**
	 * 数据库类型
	 * 
	 * @return 数据库类型
	 */
	public DbType getDbType() {
		return dbType;
	}

	/**
	 * 关系表达式,如果链接到另一个模块的一个属性则后面附加".propertyName". 譬如role_user.name
	 * 则表明role的模块链接到user模块的一个name属性.
	 * 
	 * @return 关系表达式
	 */
	public RelationExp getRelationExp() {
		if (relationExp == null) {
			if (!StrHelper.isNullOrEmpty(relationExpString)) {
				this.relationExp = new RelationExp(this, relationExpString);
			}
		}
		return relationExp;
	}

	/**
	 * 所属的结构名称.如果isSkill为true的话返回的是SkillName,否则返回的是beanName.
	 * 
	 * @return 结构名称
	 */
	public String getEntityName() {
		return entityName;
	}

	/**
	 * 是否是Skill属性.
	 * 
	 * @return true的话是Skill属性,否则属于普通模块(TBean)的属性.
	 */
	public boolean isSkill() {
		return isSkill;
	}

	/**
	 * 获取该属性所属的模块的DNA
	 * 
	 * @return DNA
	 */
	public Dna getDna() {
		return Farm.getDna(entityName);
	}

	/**
	 * 属性值的类型.根据此类型来确定属性的返回值.如果类型为自定义类,则统一为Object.
	 * 
	 * @return 属性值的类型
	 */
	public BaseFieldType getValueType() {
		return valueType;
	}

	/**
	 * 获取此属性在模块类里的setter函数方法信息
	 * 
	 * @return 方法信息
	 */
	// public Method getMethodSetter() {
	// if (methodSetter == null) {
	// String name = getName();
	// String setterName = "set" + name.substring(0, 1).toUpperCase() +
	// name.substring(1);
	// try {
	// Class<?> clazz = null;
	// if (isSkill()) {
	// SkillDna dna = SkillManager.getSkillDna(getBeanName(), getSkillName());
	// clazz = dna.getSkillClass();// 获取class
	// } else {
	// Dna dna = Farm.getDna(getBeanName());
	// clazz = dna.getBeanClass();
	// }
	// methodSetter = clazz.getMethod(setterName,
	// TypeHelper.getTypeClass(getValueType()));
	//
	// } catch (SecurityException e) {
	// CatchException.take(e);
	// } catch (NoSuchMethodException e) {
	// CatchException.take(e);
	// }
	// }
	// return methodSetter;
	// }

	/**
	 * 获取getter方法的Method对象
	 * 
	 * @return Method对象
	 */
	// public Method getMethodGetter() {
	// if (methodGetter == null) {
	// String name = getName();
	// String getterName = "get" + name.substring(0, 1).toUpperCase() +
	// name.substring(1);
	// try {
	// Class<?> clazz = null;
	// if (isSkill()) {
	// SkillDna dna = SkillManager.getSkillDna(getBeanName(), getSkillName());
	// clazz = dna.getSkillClass();// 获取class
	// } else {
	// Dna dna = Farm.getDna(getBeanName());
	// clazz = dna.getBeanClass();
	// }
	// methodGetter = clazz.getMethod(getterName,
	// TypeHelper.getTypeClass(getValueType()));
	//
	// } catch (SecurityException e) {
	// CatchException.take(e);
	// } catch (NoSuchMethodException e) {
	// CatchException.take(e);
	// }
	// }
	// return methodGetter;
	// }

	public String getDbDefaultValue() {
		return dbDefaultValue;
	}

	/**
	 * 该属性是否必填.
	 * 
	 * @return true为必填
	 */
	public boolean isRequired() {
		return isRequired;
	}

	/**
	 * 是否唯一值.
	 * 
	 * @return true为唯一.
	 */
	public boolean isUnique() {
		return isUnique;
	}

	/**
	 * 获取所属技能名称.
	 * 
	 * @return 返回技能名称.如果不属于技能属于模块(DNA)则返回null.
	 */
	public String getSkillName() {
		return skillName;
	}

	public int getDbLength() {
		return dbLength;
	}

	public boolean hasRelation() {
		boolean ret = false;
		ret = relationExp != null;
		return ret;
	}

	/**
	 * 是否是数据库字段,如果是,则不需要在数据库创建此属性的字段.一般是在自动创建数据库表的时候使用
	 * 
	 * @return 是的话返回true.
	 */
	public boolean isDbField() {
		return isDbField;
	}

	// public Method getSetter() {
	// return setter;
	// }
	//
	// public Method getGetter() {
	// return getter;
	// }

	private static DbType toDbType(String strDbType) {
		DbType dbType = null;
		if (!StrHelper.isNullOrEmpty(strDbType)) {
			dbType = DbType.valueOf(strDbType);
		}
		return dbType;
	}

	private static String getDefaultValue(PropertyConfig pc) {
		String ret = pc.getDbDefaultValue();
		if (DbTypeUtil.NULL.equals(ret)) {
			ret = null;
		}
		return ret;
	}

	private static BaseFieldType toBaseFieldType(String strFiledType) {
		BaseFieldType fieldType = null;
		if (!StrHelper.isNullOrEmpty(strFiledType)) {
			fieldType = BaseFieldType.valueOf(strFiledType);
		}
		return fieldType;
	}

	public boolean isReadOnly() {
		return isReadOnly;
	}

	public boolean isEditable() {
		return isEditable;
	}

	/**
	 * 检查是否跟配置相同.
	 * 
	 * @param config
	 *            属性配置
	 * @return 返回0表示完全相同,1表示基本属性不同,2表示数据库属性不相同.3表示基本属性和数据库属性都有不同
	 */
	public int checkDifferent(PropertyConfig config) {
		boolean db = true;
		boolean other = true;
		if (!StrHelper.safeEqual(getName(), config.getName())) {
			other = false;
		} else if (config.getIsEditable() != isEditable()) {
			other = false;
		} else if (config.isReadOnly() != isReadOnly()) {
			other = false;
		} else if (config.isRequired() != isRequired()) {
			other = false;
		} else if (config.isUnique() != isUnique()) {
			other = false;
		} else if (!StrHelper.safeEqual(config.getRelationExp(), getRelationExp().getRelationExpString())) {
			other = false;
		} else if (!StrHelper.safeEqual(config.getLabel(), getLabel())) {
			other = false;
		} else if (!StrHelper.safeEqual(config.getValueType(), getValueType().name())) {
			other = false;
		}

		if (!StrHelper.safeEqual(config.getDbDefaultValue(), getDbDefaultValue())) {
			db = false;
		} else if (!StrHelper.safeEqual(config.getDbFieldName(), getDbFieldName())) {
			db = false;
		} else if (!StrHelper.safeEqual(config.getDbType(), getDbType().name())) {
			db = false;
		} else if (config.getDbLength() != getDbLength()) {
			db = false;
		}

		int ret = 0;
		if (!other) {
			if (db) {
				ret = 2;
			} else {
				ret = 3;
			}
		} else if (!db) {
			ret = 1;
		}
		return ret;
	}

	public String getSetterName() {
		return setterName;
	}

	public String getGetterName() {
		return getterName;
	}

	public FullTextConfig getFullTextConfig() {
		return fullTextConfig;
	}
}
